package com.lxw.multidb.controller;

import cn.tannn.jdevelops.exception.built.BusinessException;
import cn.tannn.jdevelops.result.response.ResultVO;
import cn.tannn.jdevelops.result.utils.UUIDUtils;
import com.lxw.multidb.controller.dto.ColumnInfo;
import com.lxw.multidb.controller.dto.DbConnectionRequest;
import com.lxw.multidb.controller.dto.QueryTableDataRequest;
import com.lxw.multidb.controller.es.CreateIndexDTO;
import com.lxw.multidb.controller.es.ImportProgressDTO;
import com.lxw.multidb.modules.es.EsProcessServiceImpl;
import com.lxw.multidb.modules.sql.DatabaseMetaService;
import com.lxw.multidb.modules.sql.entity.DbConnection;
import com.lxw.multidb.modules.sql.service.DbConnectionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import javax.sql.DataSource;
import java.util.List;
import java.util.Map;

/**
 * ES数据导入控制器
 * 提供数据库连接、元数据查询、数据导入ES等接口
 * <p>
 * 主要功能：
 * 1. 获取数据库连接列表
 * 2. 查询数据库、表、字段元数据
 * 3. 查询表数据预览
 * 4. 异步导入数据到ES
 * 5. 查询导入任务进度
 *
 * @author lxw
 * @version V1.1
 * @date 2024-7-18
 * @updated 2025-10-31 新增任务进度查询接口
 */
@RestController
@RequestMapping("/import")
@RequiredArgsConstructor
@Tag(name = "导入数据到ES")
public class EsImportController {

    private final DbConnectionService dbConnectionService;
    private final DatabaseMetaService databaseMetaService;
    private final EsProcessServiceImpl esProcessService;

    /**
     * 获取数据链接列表
     * 返回系统中配置的所有数据库连接信息
     *
     * @return ResultVO<List < DbConnection>> 数据库连接列表
     * @author lxw
     * @date 2024-7-18
     */
    @Operation(summary = "获取数据链接列表")
    @GetMapping("/dbUrl")
    public ResultVO<List<DbConnection>> getDbUrl() {
        List<DbConnection> list = dbConnectionService.list();
        return ResultVO.success(list);
    }

    /**
     * 获取数据库列表
     * 根据数据库连接ID查询该连接下的所有数据库
     *
     * @param dto 包含数据库连接ID的DTO
     * @return ResultVO<List < String>> 数据库名称列表
     * @throws BusinessException 当数据库连接不存在时抛出
     * @author lxw
     * @date 2024-7-18
     */
    @Operation(summary = "获取数据库列表")
    @PostMapping("/databases")
    public ResultVO<List<String>> getDatabases(@RequestBody @Valid CreateIndexDTO dto) {
        DbConnection dbConnection = dbConnectionService.getById(dto.getDbId())
                .orElseThrow(() -> new BusinessException("数据库链接失败，请重新选择或添加有效数据库配置"));
        DbConnectionRequest dbConnectionReq = dbConnection.to(DbConnectionRequest.class);
        List<String> databases = databaseMetaService.listDatabases(dbConnectionReq);
        return ResultVO.success(databases);
    }

    /**
     * 获取表集合
     * 根据数据库连接ID和数据库名称查询该库下的所有表
     *
     * @param dto 包含数据库连接ID和数据库名称的DTO
     * @return ResultVO<List < String>> 表名列表
     * @throws BusinessException 当数据库连接不存在时抛出
     * @author lxw
     * @date 2024-7-18
     */
    @Operation(summary = "获取表集合")
    @PostMapping("/tables")
    public ResultVO<List<String>> getTables(@RequestBody @Valid CreateIndexDTO dto) {
        DbConnection dbConnection = dbConnectionService.getById(dto.getDbId())
                .orElseThrow(() -> new BusinessException("数据库链接失败，请重新选择或添加有效数据库配置"));
        DbConnectionRequest dbConnectionReq = dbConnection.to(DbConnectionRequest.class);
        dbConnectionReq.setDatabaseName(dto.getDbName());
        List<String> tables = databaseMetaService.listTables(dbConnectionReq);
        return ResultVO.success(tables);
    }

    /**
     * 获取表字段集合
     * 根据数据库连接ID、数据库名称、表名称查询该表的所有字段
     *
     * @param dto 包含数据库连接ID、数据库名称、表名称的DTO
     * @return ResultVO<List < String>> 字段名列表
     * @throws BusinessException 当数据库连接不存在时抛出
     * @author lxw
     * @date 2024-7-18
     */
    @Operation(summary = "获取表字段集合")
    @PostMapping("/columns")
    public ResultVO<List<String>> getColumns(@RequestBody @Valid CreateIndexDTO dto) {
        DbConnection dbConnection = dbConnectionService.getById(dto.getDbId())
                .orElseThrow(() -> new BusinessException("数据库链接失败，请重新选择或添加有效数据库配置"));
        DbConnectionRequest dbConnectionReq = dbConnection.to(DbConnectionRequest.class);
        dbConnectionReq.setDatabaseName(dto.getDbName());
        dbConnectionReq.setTableName(dto.getTableName());
        List<String> columns = databaseMetaService.listColumns(dbConnectionReq);
        return ResultVO.success(columns);
    }


    @Operation(summary = "获取表字段集合2")
    @PostMapping("/listColumnsWithMeta")
    public ResultVO<List<ColumnInfo>> listColumnsWithMeta(@RequestBody @Valid CreateIndexDTO dto) {
        DbConnection dbConnection = dbConnectionService.getById(dto.getDbId())
                .orElseThrow(() -> new BusinessException("数据库链接失败，请重新选择或添加有效数据库配置"));
        DbConnectionRequest dbConnectionReq = dbConnection.to(DbConnectionRequest.class);
        dbConnectionReq.setDatabaseName(dto.getDbName());
        dbConnectionReq.setTableName(dto.getTableName());
        List<ColumnInfo> columnInfos = databaseMetaService.listColumnsWithMeta(dbConnectionReq);
        return ResultVO.success(columnInfos);
    }

    /**
     * 查询数据
     * 根据条件查询表数据，支持分页和字段映射
     *
     * @param dto 查询条件DTO，包含表名、条件、分页参数等
     * @return ResultVO<List < Map < String, Object>>> 查询结果列表
     * @throws BusinessException 当数据库连接不存在时抛出
     * @author lxw
     * @date 2024-7-18
     */
    @Operation(summary = "查询数据")
    @PostMapping("/queryTableData")
    public ResultVO<List<Map<String, Object>>> queryTableData(@RequestBody @Valid QueryTableDataRequest dto) {
        DbConnection dbConnection = dbConnectionService.getById(dto.getDbId())
                .orElseThrow(() -> new BusinessException("数据库链接失败，请重新选择或添加有效数据库配置"));
        DbConnectionRequest dbConnectionReq = dbConnection.to(DbConnectionRequest.class);
        dbConnectionReq.setDatabaseName(dto.getDbName());
        dbConnectionReq.setTableName(dto.getTableName());
        dbConnectionReq.setCondition(dto.getCondition());
        dbConnectionReq.setFieldMappings(dto.getFieldMappings());
        dbConnectionReq.setPageIndex(dto.getPageIndex());
        dbConnectionReq.setPageSize(dto.getPageSize());

        List<Map<String, Object>> maps = databaseMetaService.queryTableData(dbConnectionReq);
        return ResultVO.success(maps);
    }

    /**
     * 数据导入ES（异步执行）
     * 创建数据源并异步导入数据到Elasticsearch
     * <p>
     * 流程说明：
     * 1. 生成唯一批次号（UUID）
     * 2. 验证数据库连接
     * 3. 创建数据源
     * 4. 异步执行导入任务
     * 5. 立即返回批次号供前端轮询查询进度
     *
     * @param dto 索引创建DTO，包含数据库连接、表信息、索引配置等
     * @return ResultVO<String> 返回任务批次号，用于查询导入进度
     * @throws BusinessException 当数据库连接不存在时抛出
     * @author lxw
     * @date 2024-7-18
     * @updated 2025-10-31 改为异步执行，返回批次号
     */
    @Operation(summary = "数据导入ES")
    @PostMapping("/importEs")
    public ResultVO<String> importEs(@RequestBody @Valid CreateIndexDTO dto) {

        // 获取数据库连接配置
        DbConnection dbConnection = dbConnectionService.getById(dto.getDbId())
                .orElseThrow(() -> new BusinessException("数据库链接失败，请重新选择或添加有效数据库配置"));
        // 转换为数据库连接请求对象
        DbConnectionRequest dbConnectionRequest = dbConnection.to(DbConnectionRequest.class);
        dbConnectionRequest.setDatabaseName(dto.getDbName());
        // 创建数据源
        DataSource dataSource = databaseMetaService.createDataSource(dbConnectionRequest);
        // 生成唯一的任务批次号
        String batchNo = UUIDUtils.getInstance().generateShortUuid();
        // 异步执行数据导入任务
        esProcessService.initEsData(batchNo, dto, dataSource);
        // 立即返回批次号，前端可以使用此批次号轮询查询进度
        return ResultVO.success(batchNo);
    }

    /**
     * 根据批次号获取任务进度
     * 前端轮询调用此接口，实时获取数据导入进度
     * <p>
     * 返回数据说明：
     * - totalCount: 总数据条数
     * - finishCount: 已完成导入的条数
     * - state: 任务状态（0-进行中，1-已完成）
     * <p>
     * 注意事项：
     * - 任务完成后缓存会被清理，此时返回100%完成状态
     * - 建议前端每隔1-2秒轮询一次
     * - state=1时可以停止轮询
     *
     * @param batchNo 任务批次号（由importEs接口返回）
     * @return ResultVO<ImportProgressDTO> 任务进度信息
     * @author lxw
     * @date 2025-10-31
     */
    @Operation(summary = "根据批次号获取任务进度")
    @GetMapping("/schedule/{batchNo}")
    public ResultVO<ImportProgressDTO> getProgress(@PathVariable String batchNo) {
        ImportProgressDTO progress = esProcessService.getProgress(batchNo);
        return ResultVO.success(progress);
    }

}